package com.mine.jzh3.oss.config;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.mine.jzh3.oss.entity.AliOSSConfig;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;

/**
 * 阿里云OSS客户端管理器<br>
 * <br>
 * 当前版本仅支持单一配置（通过 configId 识别），通过懒加载方式创建并复用 OSSClient 实例。<br>
 * 若后续需要支持多配置, 可基于 configId 为维度，将 OSSClient 实例存入 ConcurrentHashMap<String, OSS> 中进行缓存和复用。<br>
 * <br>
 * 建议扩展方式如下：<br>
 * 1. 使用 ConcurrentHashMap<String, OSS> clientCache 存储多个 client；<br>
 * 2. getClient(AliOSSConfig config) 时通过 configId 查找已有实例；<br>
 * 3. 若不存在则创建新 client，并通过 putIfAbsent 原子性缓存；<br>
 * 4. 可选地为每个 client 注册销毁钩子，或实现统一 destroy() 关闭所有缓存实例。<br>
 */
@Slf4j
@Component
public class AliOSSClientManager {
    private final Object lock = new Object();
    // volatile 的可见性 保证了 第一次非同步判断的准确性, 避免锁竞争
    private volatile OSS ossClient;
    private volatile String currentConfigId;
    @Getter
    private String bucketName;

    /**
     * 获取OSS客户端
     */
    public OSS getClient(AliOSSConfig config) {
        String configId = config.getConfigId();
        // 第一次非同步判断
        if (configId.equals(currentConfigId) && ossClient != null) {
            return ossClient;
        }

        synchronized (lock) {
            // 第二次检查，防止重复创建
            if (configId.equals(currentConfigId) && ossClient != null) {
                return ossClient;
            }

            // 销毁旧 client
            if (ossClient != null) {
                try {
                    ossClient.shutdown();
                    log.info("销毁旧 OSSClient: configId={}", currentConfigId);
                } catch (Exception e) {
                    log.warn("关闭 OSSClient 异常", e);
                }
            }

            // 使用临时变量确保完整初始化, 防止构建过程抛异常导致 ossClient 被置为空或部分初始化
            OSS newClient = new OSSClientBuilder().build(config.getEndpoint(), config.getAccessKey(), config.getSecretKey());
            // 原子性地更新所有状态
            this.ossClient = newClient;
            this.currentConfigId = configId;
            this.bucketName = config.getBucketName();
            log.info("创建新 OSSClient: configId={}, endpoint={}", configId, config.getEndpoint());

            return ossClient;
        }
    }

    /**
     * 获取当前客户端
     */
    public OSS getCurrentClient() {
        return ossClient;
    }

    @PreDestroy
    public void destroy() {
        if (ossClient != null) {
            try {
                ossClient.shutdown();
                log.info("关闭阿里云OSS客户端");
            } catch (Exception e) {
                log.error("关闭阿里云OSS客户端失败", e);
            }
        }
    }
}
